package org.dromara.system.kernel.channel;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.encrypt.utils.EncryptUtils;
import org.dromara.system.domain.PayAccount;
import org.dromara.system.domain.PayGroupAccount;
import org.dromara.system.domain.PayOrder;
import org.dromara.system.domain.PayOrderLog;
import org.dromara.system.domain.bo.OrderRelateBo;
import org.dromara.system.domain.bo.aparams.AOrderBo;
import org.dromara.system.domain.bo.companychannel2.CpCh2PushBo;
import org.dromara.system.domain.bo.companychannel2.CpCh2UpdateBo;
import org.dromara.system.domain.bo.cparams.OrderProduct;
import org.dromara.system.domain.bo.cparams.OrderUpdateBo;
import org.dromara.system.kernel.OrderUpdateKernel;
import org.dromara.system.service.IPayAccountService;
import org.dromara.system.service.IPayOrderLogService;
import org.dromara.system.service.IPayOrderService;
import org.dromara.system.utils.ConversionUtils;
import org.dromara.system.utils.RequestUtils;
import org.dromara.system.utils.UserAgentUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

@RequiredArgsConstructor
@Service
public class CompanyChannel4Kernel {

    private final IPayOrderLogService orderLogService;
    private final IPayOrderService orderService;
    private final IPayOrderService payOrderService;
    private final OrderUpdateKernel orderUpdateKernel;
    private final IPayAccountService accountService;
    @Value("${gatewayDomain}")
    public String gatewayDomain;

    /**
     * 获取支付链接
     *
     * @param relateBo 订单关联信息
     */
    public R<PayOrder> requestPayLink(OrderRelateBo relateBo) {

        PayAccount account = relateBo.getAccount();
        PayGroupAccount groupAccount = relateBo.getGroupAccount();
        PayOrder order = relateBo.getOrder();
        PayOrderLog log = relateBo.getLog();

        //获取账号请求域名
        String domain = account.getDomain();
        if (StringUtils.isEmpty(domain)) {
            return R.fail("DomainUrl is invalid", order);
        }

        //获取账号配置
        JSONObject config = account.getAccountConfig();
        if (config == null || config.isEmpty() || !config.containsKey("clientId") || !config.containsKey("secretKey") || !config.containsKey("payApi")) {
            return R.fail("Account Config is invalid", order);
        }
        String clientId = config.getString("clientId"); //商户ID
        String secretKey = config.getString("secretKey"); //商户密匙
        String payApi = config.getString("payApi"); //接口API

        //构建参数
        Map<String, String> params = buildParams(groupAccount, order, clientId, secretKey);
        if (params == null) {
            return R.fail("no config params found", order);
        }

        //请求
        long time = System.currentTimeMillis();
        String res1;
        try {
            res1 = new RequestUtils().doPostForm(domain + payApi, params);
        } catch (Exception e) {
            res1 = "请求失败：" + e.getMessage();
        }

        //记录请求日志
        orderLogService.saveLog(log.getLogId(), order.getOrderId(), order.getOrderNo(), order.getOrderAid(), "获取支付链接", JSONObject.toJSONString(params, JSONWriter.Feature.WriteMapNullValue), res1, System.currentTimeMillis() - time);

        try {
            if (StringUtils.isEmpty(res1) || res1.startsWith("请求失败")) {
                return R.fail("Request link null", order);
            }

            if (!res1.contains("<hjh>")) {
                return R.fail(res1, order);
            }

            String payUrl = gatewayDomain + "/pay/companyChannel4.html?result1=" + URLEncoder.encode(res1, StandardCharsets.UTF_8);

            //设置订单参数
            AOrderBo.Address bill = JSONObject.parseObject(order.getCustomer(), AOrderBo.Address.class);
            payUrl += "&BillFirstName=" + URLEncoder.encode(bill.getFirstName(), StandardCharsets.UTF_8) +
                "&BillLastName=" + URLEncoder.encode(bill.getLastName(), StandardCharsets.UTF_8) +
                "&BillAddress1=" + URLEncoder.encode(bill.getAddress1(), StandardCharsets.UTF_8) +
                "&BillAddress2=" + URLEncoder.encode(bill.getAddress2(), StandardCharsets.UTF_8) +
                "&BillCity=" + URLEncoder.encode(bill.getCity(), StandardCharsets.UTF_8) +
                "&BillState=" + URLEncoder.encode(bill.getState(), StandardCharsets.UTF_8) +
                "&BillCountry=" + URLEncoder.encode(bill.getCountry(), StandardCharsets.UTF_8) +
                "&BillPhone=" + URLEncoder.encode(bill.getPhone(), StandardCharsets.UTF_8) +
                "&BillZip=" + URLEncoder.encode(bill.getPostcode(), StandardCharsets.UTF_8) +
                "&BillEmail=" + URLEncoder.encode(bill.getEmail(), StandardCharsets.UTF_8);


            //写到订单对象里，返出去使用
            order.setLink(payUrl);

            //更新订单
            PayOrder up = new PayOrder();
            up.setOrderId(order.getOrderId());
            up.setLink(payUrl);
            orderService.updatePayOrderAsync(up);

            //记录更新日志
            orderLogService.saveLog(log.getLogId(), order.getOrderId(), order.getOrderNo(), order.getOrderAid(), "更新B端订单信息", JSONObject.toJSONString(up), "success", 10L);

            return R.ok(order);

        } catch (Exception e) {
            return R.fail("Request link fail", order);
        }
    }

    /**
     * 更新订单
     *
     * @param orderNo 参数
     * @return JSONObject
     */
    public R<?> updateOrder(String orderNo, Integer status) {

        Long orderId = Long.parseLong(orderNo.split("_")[0]);

        PayOrder order = payOrderService.selectById(orderId);
        if (order == null) {
            return R.fail("Order does not exist");
        }

        //更新订单参数
        OrderUpdateBo bo = new OrderUpdateBo(orderId, status == 1 ? 2 : 3, null);

        //更新订单
        return orderUpdateKernel.updateOrder(order, bo, new JSONObject().fluentPut("orderNo", orderNo).fluentPut("status", status).toJSONString());
    }

    /**
     * 构建请求参数
     *
     * @param order 订单
     * @return JSONObject
     */
    private Map<String, String> buildParams(PayGroupAccount groupAccount, PayOrder order, String clientId, String secretKey) {

        String currency; //币别
        BigDecimal amount; //金额
        if (Objects.equals(groupAccount.getIsForce(), 1) && order.getForceCurrency() != null) {
            currency = order.getForceCurrency();
            amount = order.getForceMoney();
        } else {
            currency = order.getCurrency();
            amount = order.getCMoney();
        }

        amount = amount.setScale(2, RoundingMode.HALF_UP);

        AOrderBo.Address bill = JSONObject.parseObject(order.getCustomer(), AOrderBo.Address.class);
        AOrderBo.Address shipping = JSONObject.parseObject(order.getShipping(), AOrderBo.Address.class);
        AOrderBo.Device device = JSONObject.parseObject(order.getDevice(), AOrderBo.Device.class);
        if(device == null) {
            device = new AOrderBo.Device();
        }
        String orderNo = order.getOrderId() + "_" + ConversionUtils.generateShortCode(order.getOrderId() + "", 6);

        Map<String, String> params = new HashMap<>();
        params.put("yhm", clientId);
        params.put("miyao", secretKey);
        params.put("Amount", amount.toPlainString());
        params.put("wztjsj", DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, order.getCreateTime()));
        params.put("qxUrl", order.getCancelUrl());
        params.put("cgtzdzUrl", gatewayDomain + "/api/system/external/updateCpCh4");
        params.put("orderid", orderNo);
        params.put("wzje", amount.toPlainString());
        params.put("kz", "wpnq");
        params.put("ym", gatewayDomain);
        params.put("llqmc", StringUtils.isNotEmpty(device.getBr()) ? device.getBr() : "Chrome");
        params.put("llqyy", StringUtils.isNotEmpty(device.getLang()) ? device.getLang() : "en-US");
        params.put("czxt", StringUtils.isNotEmpty(device.getOs()) ? device.getOs() : "Windows");
        params.put("CurrencyCode", currency);
        params.put("Remark", "");
        params.put("ddid", orderNo);
        params.put("IpAddress", order.getCustomerIp());
        params.put("BillCountry", bill.getCountry());
        params.put("BillState", bill.getState());
        params.put("BillCity", bill.getCity());
        params.put("BillAddress", bill.getAddress1() + " " + bill.getAddress2());
        params.put("BillZip", bill.getPostcode());
        params.put("BillEmail", bill.getEmail());
        params.put("BillFirstName", bill.getFirstName());
        params.put("BillLastName", bill.getLastName());
        params.put("BillPhone", bill.getPhone());
        params.put("ShipFirstName", shipping.getFirstName());
        params.put("ShipLastName", shipping.getLastName());
        params.put("ShipCountry", shipping.getCountry());
        params.put("ShipState", shipping.getState());
        params.put("ShipCity", shipping.getCity());
        params.put("ShipAddress", shipping.getAddress1() + " " + shipping.getAddress2());
        params.put("ShipZip", shipping.getPostcode());
        params.put("ShipEmail", shipping.getEmail());
        params.put("ShipPhone", shipping.getPhone());

        //产品信息
        StringBuilder prods = new StringBuilder();
        List<OrderProduct> products = JSONArray.parseArray(order.getProduct(), OrderProduct.class);
        for (OrderProduct product : products) {
            prods.append("{").append(product.getQuantity()).append(" X ").append(product.getName()).append("}");
        }
        params.put("GoodsJson", prods.toString());

        return params;
    }

    public static String genInvoiceId() {
        String token = StringUtils.randomCode(18);
        return md5Hex(UUID.randomUUID().toString()) + token;
    }

    private static String md5Hex(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] messageDigest = md.digest(input.getBytes());
            StringBuilder hexString = new StringBuilder();
            for (byte b : messageDigest) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) hexString.append('0');
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            return "";
        }
    }
}
